前言
咕咕咕了半个月。。
这次从通过 LWRP 的 Render Features 实现遮挡显示轮廓效果来让大家对渲染管线有一个大致的轮廓。
参考链接
- 使用Unity2019 中LWRP,可以非常轻松地对被墙壁遮挡的角色进行剪影
- Introduction to the Lightweight Render Pipeline - Unity at GDC 2019
介绍
效果:
是一个当固定层级物体被遮挡后显示轮廓的功能,使用 Shader 也可以完成,使用 LWRP 的 Render Features 的话完成的更直观一些。
步骤
此次运行版本需要 2019 四月份以后的版本,我选择的是 2019.1.9f1,在创建项目时直接创建 LWRP项目, LWRP 的版本要求是 5.7 以上,在 Windows—>Package Manager 中下载。
需要勾选 show preview package 才能显示预览版的功能包。
分别创建 管线资源 和 前向渲染器
在 Project Setting 中 将创建好的渲染管线拖入其中,把 LWRP资源的 渲染模式改为 Custom 并把创建的 前向渲染器资源 拖到 LWRP 资源中。
新建一个场景,放置几个资源,会发现新建的 3D GameObjcet 使用了不同的 默认 Shader,这是由于更换了渲染管线,具体的 默认Shader 也随之改变了。
在 前向渲染器 中添加如图所示的一个 RendererFeature,其中的材质是选择的一个 Unity 自带的默认材质,并把如图后面两个资源改为 Character 层级,得到的效果就像第二幅图一样,有个地方不尽人意,就是同为 Character 层级,也出现了遮挡后显示轮廓的效果。
如图所示,将 ForwardRender 再新建一个 RenderFeatrues,并把 前向渲染器的 默认层级遮罩 取消掉勾选 Character 层级,就大致可以得到文章开头的效果了。
存在一个问题,就是同层级的物体之间还是可能会有出现遮挡显示轮廓的效果,但我们可以把他们当做一体选择一个只有颜色没有轮廓的 材质 来做遮挡显示。
讲解
前面的创建与设置过程不做讲解,主要是一些默认操作,很多参数也是保持了默认。主要是对效果实现的讲解。
原理科普
渲染队列
场景中有一个一个的 GameObject(下面简称为 GO),对于这些 GO,我们要根据其身上的材质进行渲染,这里有一个问题,即先渲染哪一个后渲染哪一个,不要以为渲染顺序不会影响最终效果,渲染顺序透明度实现上会产生很大的影响。
而这里使用 Rendere Feature 强行改变了渲染队列,以及后续渲染采用的 材质。
深度值
即 Z。摄像机在游戏中相当于我们的眼睛,我们只能看到眼睛最前面的片元A,而看不到挡在后面的片元B,就是说B离我们远,抽象到渲染中就是 Z 值比较大,这样的话我们可以在渲染物体时判断其 Z值 是否大于当前存储的 Z值,如果大于就不渲染,如果小于就渲染,并把存储的 Z值 更新。
深度测试通过,深度写入开启:写入深度缓冲区,写入颜色缓冲区
深度测试通过,深度写入关闭:不写深度缓冲区,写入颜色缓冲区
深度测试失败,深度写入开启:不写深度缓冲区,不写颜色缓冲区
深度测试失败,深度写入关闭:不写深度缓冲区,不写颜色缓冲区
ZTest 深度值测试,通过测试才进行渲染
ZTest Less:深度小于当前缓存则通过
ZTest Greater:深度大于当前缓存则通过
ZTest LEqual/on:深度小于等于当前缓存则通过
ZTest GEqual:深度大于等于当前缓存则通过
ZTest Equal:深度等于当前缓存则通过
ZTest NotEqual:深度不等于当前缓存则通过
ZTest Always/off:不论如何都通过ZWrite on/off 深度值写入,当渲染通过是是否写入新的深度值
在 Renderer Feature 中,也是将这个深度测试与深度写入单独抽出来,在外部进行更改。
面板分析
让我们分析一下这个面板
- Deafult Layer Mask,这个分层级进行渲染,如果我们选择 Nothing,那么所有的层级都不会被渲染
- Overrids,这个是指覆盖,即通过筛选之后得到需要进行操作的部分进行覆盖绘制,Stencil 这里没有用到,后续会进行讲解。
- Renderer Features
- Name,Renderer Features 的名字
- Event,指在什么时候进行操作
- Fliters
- Queue,队列,Opaque 指筛选不透明的队列
- Layer Mask,筛选 Character 层级
- Shader Passes,具体使用到的 Shader Pass
- Overrieds 覆盖绘制
- Material 覆盖绘制的材质
- Depth 深度
- 是否把深度写入
- 是否测试深度,这里选择深度大于渲染过程中的深度值才进行渲染,即当物体深度值比较大被其他物体遮挡住的时候被渲染,也就是只有被遮挡的部分才会被渲染
- 其余待后续测试
效果实现
实际上我们分为三个步骤
对遮挡部分进行渲染,然后发现被同一层级遮挡的部分也被渲染了;
然后取消一开始对 Character 层级的渲染,即在 前向渲染器中 的 Deafult Layer Mask 取消对 Character 的勾选;
然后发现不被遮挡的部分不会被渲染,这个时候我们选择正常根据深度值缓存来渲染出不被遮挡的部分。
总结
其实使用 Shader 我们一样可以实现相同的效果,甚至在原理上也差不多,但是使用编写的方式相当于改变了 Shader 效果本身,使用 RenderFeatures 相当于在外部来更改其一些参数来使用,把 Shader、材质当做对象在不同的条件下调用可以说是 渲染管线 的一部分责任。